package org.planx.xmlstore.routing;

import java.io.*;
import java.net.*;
import java.security.*;
import java.util.*;
import java.util.concurrent.ConcurrentHashMap;
import junit.framework.*;
import tokyocabinet.HDB;

public class HashCalculatorTest extends RoutingTestCase {
    private static final long UNIT = HashCalculator.UNIT_INTERVAL;
    private static final Serializable DUMMY = Boolean.TRUE;

    protected Node local;
    protected Space space;
    protected Node node;
    protected HashCalculator hasher;
    protected HDB localMap;
    protected StoreUtil localMapX;

    public HashCalculatorTest(String s) {
        super(s);
    }

    public static Test suite() {
        return new TestSuite(HashCalculatorTest.class);
    }

    public static void main(String args[]) {
        junit.textui.TestRunner.run(HashCalculatorTest.class);
    }

    protected void setUp() {
        try {
            local = new Node(InetAddress.getLocalHost(), 4000,
                                Identifier.randomIdentifier());
            space = new Space(local, new Configuration());
            node = new Node(InetAddress.getLocalHost(), 4000,
                               Identifier.randomIdentifier());
            space.insertNode(node);

            localMap = new HDB();
            localMapX = new StoreUtil(localMap);

            hasher = new HashCalculator(local, space, localMap);
        } catch (Exception e) {
            throw new RuntimeException(e);
        }
    }

    public void testLogarithmicHashes() throws Exception {
        // Create random keys
        Identifier[] keys = new Identifier[11];
        for (int i=0;i<10;i++) keys[i] = Identifier.randomIdentifier();

        localMapX.put(keys[0], new TimestampedValue(DUMMY, (long) 99*UNIT));
        localMapX.put(keys[1], new TimestampedValue(DUMMY, (long) (98.5*UNIT)));
        localMapX.put(keys[2], new TimestampedValue(DUMMY, (long) 97*UNIT));
        localMapX.put(keys[3], new TimestampedValue(DUMMY, (long) 96*UNIT));
        localMapX.put(keys[4], new TimestampedValue(DUMMY, (long) 95*UNIT));
        localMapX.put(keys[5], new TimestampedValue(DUMMY, (long) 94*UNIT));
        localMapX.put(keys[6], new TimestampedValue(DUMMY, (long) 93*UNIT));
        localMapX.put(keys[7], new TimestampedValue(DUMMY, (long) 92*UNIT));
        localMapX.put(keys[8], new TimestampedValue(DUMMY, (long) 91*UNIT));
        localMapX.put(keys[9], new TimestampedValue(DUMMY, (long) 90*UNIT));

        List result = hasher.logarithmicHashes(node, 100*UNIT);

        List expected = new ArrayList();

        byte[] dummy = toByteArray(DUMMY);

        // Empty
        MessageDigest md = MessageDigest.getInstance("MD5");
        expected.add(md.digest());

        // Bundle size: 1
        md.reset();
        md.update(keys[0].toByteArray());
        md.update(dummy);
        expected.add(md.digest());

        // Bundle size: 2
        md.reset();
        md.update(keys[1].toByteArray());
        md.update(dummy);
        md.update(keys[2].toByteArray());
        md.update(dummy);
        expected.add(md.digest());

        // Bundle size: 4
        md.reset();
        md.update(keys[3].toByteArray());
        md.update(dummy);
        md.update(keys[4].toByteArray());
        md.update(dummy);
        md.update(keys[5].toByteArray());
        md.update(dummy);
        md.update(keys[6].toByteArray());
        md.update(dummy);
        expected.add(md.digest());

        // Bundle size: 3 (rest)
        md.reset();
        md.update(keys[7].toByteArray());
        md.update(dummy);
        md.update(keys[8].toByteArray());
        md.update(dummy);
        md.update(keys[9].toByteArray());
        md.update(dummy);
        expected.add(md.digest());

        for (int i=0,max=result.size(); i<max; i++) {
//            System.out.println("result["+i+"]="+toString((byte[])result.get(i)));
            assertEquals(toString((byte[])expected.get(i)), toString((byte[])result.get(i)));
        }
    }

    private static byte[] toByteArray(Object o) throws IOException {
        ByteArrayOutputStream bout = new ByteArrayOutputStream();
        ObjectOutputStream oout = new ObjectOutputStream(bout);
        oout.writeObject(o);
        oout.close();
        return bout.toByteArray();
    }
}
